package com.ggkt.security;

import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Security;

/***
 *  使用说明
 *  JDK1.8-191 以上版本，低版本下使用 256bit 需安装扩展包
 *
 *  POM 需添加算法扩展包，版本号参考项目版本Springboot版本
 *  <dependency>
 * 			<groupId>org.bouncycastle</groupId>
 * 			<artifactId>bcprov-jdk15on</artifactId>
 * 			<version>1.55</version>
 * 	</dependency>　

 *      base64编解码类 sun.misc.BASE64Decoder，sun.misc.BASE64Encoder
 *      JDK9以及以上版本，不支持；
 *      为了保持良好兼容性，换用算法扩展包 Base64相应工具类；
 *      org.bouncycastle.util.encoders.Base64
 *     新增AESEncryptV2，AESDecryptV2两个函数，正式环境使用，
 *     密钥散列生成转Hex字符串，安全级别更高；
 *
 *
 * */

@Slf4j
public class CryptoUtil {
    /***
     * 常量，IV向量，密钥,
     * 方便调试，生产环境，密钥，向量，需外部获取；
     * */
    private static final String KEY_ALGORITHM = "AES";
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";  // JDK默认的加密算法，填充方式
    private static final String DEFAULT_CIPHER_ALGORITHM_EXTEND = "AES/CBC/PKCS7Padding";  // 默认的加密算法，JCE扩展填充方式
    private static final String DEFAULT_CIPHER_ALGORITHM_THIRD_EXTEND = "AES/CBC/PKCS5Padding";  // JDK默认的加密算法，填充方式
    private static final String CHARSET = "UTF-8";
    private static final String IV = "ggkt369#5(1p_1^8";                            //初始化向量参数，AES 16 bytes 测试环境0123456789abcdef
    private static final String KEY128 = "0123456789012345";                       // 128bits 16 bytes AES key
    private static final String KEY256 = "01234567890123456789012345678912";        // 256bits 32 bytes AES key
    // 256bits 32 bytes AES key，散列随机生成转码HEX，更安全  测试环境 a861ffe2c66798f3eadfa6b301c11f29fc78d309e0406c37f00ad8c883cb302a
    private static final String HexKey = "7c97212ea87a5f5d915eb7e42d98c7844327479971893c1eda34660d8b929f7c";

    // 扩展算法支持PKCS7Padding填充
    static {
        if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) {
            Security.addProvider(new BouncyCastleProvider());
        }
    }


    /***
     * ASE 加密
     *
     * @param sSrc      明文
     * @param  //sKey      密钥
     * @param // sIv       向量
     * @return String
     *
     * */
    public static String AESEncrypt(String sSrc/*, String sKey, String sIv*/) throws Exception {

        if (sSrc == null) {
            System.out.print("加密内容为空null");
            return null;
        }

        try {
            byte[] raw = KEY256.getBytes(CHARSET);
            SecretKeySpec skeySpec = new SecretKeySpec(raw, KEY_ALGORITHM);
            IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes());

            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);//"算法/模式/补码方式"
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec);

            byte[] encrypted = cipher.doFinal(sSrc.getBytes(CHARSET));

            // 二进制转BASE64，string。
            return Byte2Base64(encrypted);

        } catch (Exception e) {
            System.out.println(e.toString());
            return null;
        }

    }

    /***
     * ASE 解密
     *
     * @param sSrc      密文
     * @param // sKey      密钥
     * @param // sIv       向量
     * @return String
     *
     * */
    public static String AESDecrypt(String sSrc/*, String sKey, String sIv*/) throws Exception {

        if (sSrc == null) {
            System.out.print("解密内容为空null");
            return null;
        }

        try {

            byte[] raw = KEY256.getBytes(CHARSET);
            SecretKeySpec skeySpec = new SecretKeySpec(raw, KEY_ALGORITHM);
            IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes());

            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);

            byte[] encrypted = Base642bByte(sSrc);// 密文BASE64转BYTE

            byte[] original = cipher.doFinal(encrypted);
            String originalString = new String(original, CHARSET);

            return originalString;

        } catch (Exception e) {
            System.out.println(e.toString());
            return null;
        }
    }

    /***
     * ASE 加密 V2，安全性更高，线上环境使用此版本
     * Key密钥：散列后的Hex字符串，安全性更高
     *
     * @param sSrc      明文
     * @param // sKey      HEX编码密钥 64字符
     * @param // sIv       向量
     * @return String
     *
     * */
    public static String AESEncryptV2(String sSrc/*, String sKey, String sIv*/) throws Exception {

        if (sSrc == null) {
            System.out.print("加密内容为空null");
            return null;
        }

        //去除空格
        sSrc = sSrc.trim();
        sSrc = sSrc.replaceAll((char)12288+"","");
        
        if (sSrc == null || "".equals(sSrc)) {
            System.out.print("加密内容为空null");
            return null;
        }

        try {
            byte[] raw = HexToByte(HexKey);
            SecretKeySpec skeySpec = new SecretKeySpec(raw, KEY_ALGORITHM);
            IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes());

            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM_EXTEND);//"算法/模式/补码方式"
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivSpec);

            byte[] encrypted = cipher.doFinal(sSrc.getBytes(CHARSET));

            // 二进制转BASE64，string。
            return Byte2Base64(encrypted);

        } catch (Exception e) {
            System.out.println(e.toString());
            log.error("AESEncryptV2加密异常,加密字符串:{},异常内容:{}",sSrc,e.toString());
            return null;
        }

    }


    /***
     * ASE 解密 V2, 安全性更高，线上环境使用此版本
     * Key密钥：散列后的Hex字符串，安全性更高
     *
     * @param sSrc      密文
     * @param // sKey      HEX编码密钥 64字符
     * @param // sIv       向量
     * @return String
     *
     * */
    public static String AESDecryptV2(String sSrc/*, String sKey, String sIv*/) throws Exception {

        if (sSrc == null || "".equals(sSrc.trim())) {
            System.out.print("解密内容为空null");
            return null;
        }
        sSrc = sSrc.trim();

        try {

            byte[] raw = HexToByte(HexKey);
            SecretKeySpec skeySpec = new SecretKeySpec(raw, KEY_ALGORITHM);
            IvParameterSpec ivSpec = new IvParameterSpec(IV.getBytes());

            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM_EXTEND);
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);

            byte[] encrypted = Base642bByte(sSrc);// 密文BASE64转BYTE

            byte[] original = cipher.doFinal(encrypted);
            String originalString = new String(original, CHARSET);

            return originalString;

        } catch (Exception e) {
            System.out.println(e.toString());
            log.error("AESDecryptV2解密异常,解密字符串:{},异常内容:{}",sSrc,e.toString());
            return null;
        }
    }

    /**
     * @Description: ASE 解密  使用CBC加密模式，填充使用PKCS5Padding，使用128位数据块
     * @Author: xuhuibin
     * @Date: 2021/7/20 17:50
     * @Param: sSrc
     * @Return: String
     */
    public static String AESEncryptV3(String sSrc) throws Exception {

        if (sSrc == null) {
            return null;
        }

        sSrc = sSrc.replaceAll(" ","");
        sSrc = sSrc.replaceAll((char)12288+"","");

        if (sSrc == null || "".equals(sSrc)) {
            return null;
        }

        try {
            IvParameterSpec zeroIv = new IvParameterSpec(IV.getBytes());
            SecretKeySpec key = new SecretKeySpec(KEY128.getBytes(), KEY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM_THIRD_EXTEND);
            cipher.init(Cipher.ENCRYPT_MODE, key, zeroIv);
            byte[] encryptedData = cipher.doFinal(sSrc.getBytes(CHARSET));

            // 二进制转BASE64，string。
            return Byte2Base64(encryptedData);
        } catch (Exception e) {
            System.out.println(e.toString());
            log.error("AESEncryptV3加密异常,加密字符串:{},异常内容:{}",sSrc,e.toString());
            return null;
        }
    }

    /**
     * BASE64 编码
     *
     * @param bstr
     * @return String
     */
    private static String Byte2Base64(byte[] bstr) {
        // JDK8 已上不支持，提升兼容性，用第三方扩展包
//        return new BASE64Encoder().encode(bstr);
        return org.bouncycastle.util.encoders.Base64.toBase64String(bstr);
    }


    /**
     * BASE64 解码
     *
     * @param str
     * @return string
     */
    private static byte[] Base642bByte(String str) {
        byte[] bt = null;
//  JDK8 已上不支持，提升兼容性，换用第三方扩展包
//        try {
//            BASE64Decoder decoder = new BASE64Decoder();
//            bt = decoder.decodeBuffer(str);
//
//        } catch (IOException e) {
//            e.printStackTrace();
//        }

        bt = org.bouncycastle.util.encoders.Base64.decode(str);

        return bt;
    }

    /**
     * Hex字符串转
     *
     * @param src
     * @return byte[]
     */
    private static byte[] HexToByte(String src) {
        if (src == null || src.length() < 1) {
            return null;
        }
        byte[] ret = new byte[src.length() / 2];
        for (int i = 0; i < src.length() / 2; i++) {
            int high = Integer.parseInt(src.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(src.substring(i * 2 + 1, i * 2 + 2), 16);
            ret[i] = (byte) (high * 16 + low);
        }
        return ret;
    }

    public static void main(String[] args) throws Exception {
    	System.out.println("----------测试开始-----------------");
        String result = CryptoUtil.AESEncryptV2("15801022949");
        System.out.println("result：" + result);

        System.out.println(CryptoUtil.AESDecryptV2("+XpoDf/tHXzbHDhmMS8+Mw=="));
        System.out.println(CryptoUtil.AESDecryptV2("M9rZ2H6bO9qPHj/po12BZg=="));
        System.out.println(CryptoUtil.AESDecryptV2("WNsb/DocV7l9k7PWLC9OMQ=="));

        String result1 = CryptoUtil.AESDecryptV2("M9rZ2H6bO9qPHj/po12BZg==");
        System.out.println("Before 脱敏: "+result1);
        result1 = MyUtils.obfuscatePhone(result1);
        System.out.println("After 脱敏: "+result1);

//        System.out.println("result2：#"+result2+"#");
        System.out.println("----------测试结束-----------------");
        
    }

}
